package com.csw.android.ffmpegtest.media.base

import com.csw.android.dev_utils.ex.CloseableEx.closeIgnoreException
import java.io.File
import java.io.FileInputStream
import java.io.InputStream

object AACHelper {

    /**
     * 获取AAC文件长度
     * @return 毫秒
     *
     * 怎么计算aac文件总播放时长
     *
     * 采样率，表示单位时间内的采样次数，通常有11025 16000 22050 44100 48000等
     * 以44100为例，1秒钟采样44.1次，产生44.1帧数据。
     * 解析aac 一帧的ADTS头，得到音频的采样率。遍历整个文件，获取帧数，总帧数/44.1即播放时长（秒）
     * 万一文件很大，一帧一帧去解析计算帧数好像不是很靠谱啊。
     *
     * 另一种方式进行大致估算，因为编码时固定了比特率，而每秒采样率也是固定的，所以文件每一秒的数据长度都是一致的。
     * 根据采样率，比如44.1，读取44帧数据，取得44帧数据文件总大小/44*44.1，最终得到一秒的acc文件大小，然后文件
     * 总大小/每秒文件大小，即可估算出大致的播放时长。
     */
    fun getAACDuration(filePath: String): Long {
        var fis: FileInputStream? = null
        try {
            File(filePath).let {
                if (it.exists()) {
                    val fileLength = it.length()
                    fis = FileInputStream(it).apply {
                        var sampleRateInHz = 0
                        var frameCount = 0
                        var frame: AACADTSFrame?
                        var frameLenCount = 0
                        //读取前一秒数据，计算一秒的文件长度
                        while (sampleRateInHz >= 0) {
                            frame = readFrameData(this)
                            if (frame == null) {
                                break
                            }
                            if (sampleRateInHz == 0) {
                                sampleRateInHz = frame.aacADTSHeader.getSampleRateInHz()
                            }
                            frameCount++
                            frameLenCount += frame.aacADTSHeader.aacFrameLength
                            if (sampleRateInHz > 0 && frameCount >= sampleRateInHz / 1000) {
                                break
                            }
                        }
                        if (sampleRateInHz > 0) {
                            //计算每帧的平均字节数，然后计算一秒的字节数
                            val fileLengthInOneSecond =
                                frameLenCount * 1f / frameCount * (sampleRateInHz / 1000f)
                            val duration = fileLength * 1000L / fileLengthInOneSecond
                            return duration.toLong()
                        }
                    }
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            fis.closeIgnoreException()
        }
        return 0
    }

    fun readFrameData(inputStream: InputStream): AACADTSFrame? {
        var byte: Int
        var findStep = 0
        val header = AACADTSHeader()
        var channelConfiguration = 0
        var aacFrameLength = 0
        while (inputStream.read().apply { byte = this } != -1) {
            when (findStep) {
                0 -> {
                    if (byte == 0b11111111) {
                        findStep = 1
                    }
                }
                1 -> {
                    if (byte.shr(4) == 0b1111) {
                        findStep = 2
                        header.id = byte.shr(3).and(1)
                        header.layer = byte.shr(1).and(0b11)
                        header.protectionAbsent = byte.and(1)
                    } else {
                        findStep = 0
                    }
                }
                2 -> {
                    findStep = 3
                    header.profile = byte.shr(6)
                    header.samplingFrequencyIndex = byte.shr(2).and(0b1111)
                    header.privateBit = byte.shr(1).and(1)
                    channelConfiguration = channelConfiguration.or(byte.and(1).shl(2))
                }
                3 -> {
                    findStep = 4
                    channelConfiguration = channelConfiguration.or(byte.shr(6))
                    header.channelConfiguration = channelConfiguration
                    header.originalCopy = byte.shr(5).and(1)
                    header.home = byte.shr(4).and(1)
                    header.copyrightIdentificationBit = byte.shr(3).and(1)
                    header.copyrightIdentificationStart = byte.shr(2).and(1)
                    aacFrameLength = aacFrameLength.or(byte.and(0b11).shl(11))
                }
                4 -> {
                    findStep = 5
                    aacFrameLength = aacFrameLength.or(byte.shl(3))
                }
                5 -> {
                    findStep = 6
                    aacFrameLength = aacFrameLength.or(byte.shr(5))
                    header.aacFrameLength = aacFrameLength
                    if (byte.and(0b11111) != 0b11111) {
                        findStep = 0
                    }
                }
                6 -> {
                    if (byte.shr(2) != 0b111111) {
                        findStep = 0
                    } else {
                        header.numberOfRawDataBlocksInFrame = byte.and(0b11)
                        findStep = 7
                        break
                    }
                }
            }
        }
        if (findStep == 7) {
            val realDataLength = header.aacFrameLength - 7
            if (realDataLength > 0) {
                val realData = ByteArray(realDataLength)
                val len = inputStream.read(realData)
                if (len == realDataLength) {
                    return AACADTSFrame(header, realData)
                }
            }
        }
        return null
    }

}

/**
 * ADTS头（7个字节 56bit）
 *
 * adts_fixed_header 固定头 28bit
 * bit  定义
 * 12   syncword 固定11111111 1111
 * 1    id 0=MPEG-4 1=MPEG-2
 * 2    layer 固定00
 * 1    protection_absent 标识是否进行误码校验。0表示有CRC校验，1表示没有CRC校验
 * 2    profile 标识使用哪个级别的AAC。0: AAC Main 1:AAC LC (Low Complexity) 2:AAC SSR (Scalable Sample Rate) 3:AAC LTP (Long Term Prediction)
 * 4    sampling_frequency_index：标识使用的采样率的下标
 * 1    private_bit 私有位，编码时设置为0，解码时忽略
 * 3    channel_configuration：标识声道数
 * 1    original_copy 编码时设置为0，解码时忽略
 * 1    home 编码时设置为0，解码时忽略
 *
 * adts_variable_header 可变头 28bit
 * 1    copyright_identification_bit 编码时设置为0，解码时忽略
 * 1    copyright_identification_start 编码时设置为0，解码时忽略
 * 13   aac_frame_length 一帧的数据长度，包含帧头(7字节)
 * 11   adts_buffer_fullness 固定11111111111
 * 2    number_of_raw_data_blocks_in_frame 00 表示当前帧有value+1个原始帧
 */
class AACADTSHeader() {

    companion object {
        val SampleRateInHzIndexTable = intArrayOf(
            96000, 88200, 64000, 48000, 44100,
            32000, 24000, 22050, 16000, 12000,
            11025, 8000, 7350,
        )
    }

    /**
     * 0=MPEG-4 1=MPEG-2
     */
    var id: Int = 0
    var layer: Int = 0
    var protectionAbsent: Int = 0

    /**
     * 0: AAC Main 1:AAC LC (Low Complexity) 2:AAC SSR (Scalable Sample Rate) 3:AAC LTP (Long Term Prediction)
     */
    var profile: Int = 0

    /**
     * 采样率下标
     */
    var samplingFrequencyIndex: Int = 0
    var privateBit: Int = 0
    var channelConfiguration: Int = 0
    var originalCopy: Int = 0
    var home: Int = 0

    var copyrightIdentificationBit: Int = 0
    var copyrightIdentificationStart: Int = 0
    var aacFrameLength: Int = 0
    var numberOfRawDataBlocksInFrame: Int = 0

    fun setSampleRateInHz(sampleRateInHz: Int): AACADTSHeader {
        //采样率对应的下标
        samplingFrequencyIndex = SampleRateInHzIndexTable.indexOf(sampleRateInHz)
        if (samplingFrequencyIndex == -1) {
            samplingFrequencyIndex = 0xF
        }
        return this
    }

    fun getSampleRateInHz(): Int {
        return if (samplingFrequencyIndex >= 0
            && samplingFrequencyIndex < SampleRateInHzIndexTable.size
        ) {
            SampleRateInHzIndexTable[samplingFrequencyIndex]
        } else {
            0
        }
    }

    fun setDataSize(len: Int): AACADTSHeader {
        aacFrameLength = len + 7
        return this
    }

    /**
     * 生成7字节头部数据
     */
    fun genHeadData(): ByteArray {
        val head = ByteArray(7)
        head[0] = 0b11111111.toByte()
        head[1] = (0b11110000
                or id.shl(3)
                or layer.shl(1)
                or protectionAbsent).toByte()
        head[2] = (profile.shl(6)
                or samplingFrequencyIndex.shl(2)
                or privateBit.shl(1)
                or channelConfiguration.shr(2)
                ).toByte()
        head[3] = (channelConfiguration.and(0b11).shl(6)
                or originalCopy.shl(5)
                or home.shl(4)
                or copyrightIdentificationBit.shl(3)
                or copyrightIdentificationStart.shl(2)
                or aacFrameLength.shr(11)
                ).toByte()
        head[4] = aacFrameLength.shr(3).and(0b11111111).toByte()
        head[5] = (aacFrameLength.and(0b111).shl(5)
                or 0b11111
                ).toByte()
        head[6] = (0b11111100 or numberOfRawDataBlocksInFrame).toByte()
        return head
    }

    /**
     * https://blog.csdn.net/chailongger/article/details/84378721
     */
    fun getESDS(): ByteArray {
        return byteArrayOf(
            ((profile + 1).shl(3)
                    or samplingFrequencyIndex.shr(1)).toByte(),
            (samplingFrequencyIndex.and(1).shl(7)
                    or channelConfiguration.shl(3)).toByte()
        )
    }
}

class AACADTSFrame(val aacADTSHeader: AACADTSHeader, val data: ByteArray) {
}